﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace Prsrp
{
    /// <summary>
    /// This class is the main container for the game.
    /// </summary>
    public class GameEngine : BaseStateMachine
    {
        public void HandleKeyboardInputs()
        {
            // When user press "E", spawn a Zombie at a random location.
            if (KeyboardManager.IsKeyPressedOnce(Keys.E))
            {
                Random pRandom = new Random();
                float iXPos = (float)pRandom.NextDouble() * GameGlobals.GameWidth;
                mpCurMap.Enemies.Add(new Zombie(new Vector2(iXPos, 0)));
            }

            // When user press "A", spawn an AmmoCrate at a random location.
            if (KeyboardManager.IsKeyPressedOnce(Keys.A))
            {
                Random pRandom = new Random();
                float iXPos = (float)pRandom.NextDouble() * GameGlobals.GameWidth;
                mpCurMap.Collectables.Add(new AmmoCrate(new Vector2(iXPos, 0)));
            }

            /*
            // Toogle cage visibility if needed.
            if (IsKeyPressedOnce(Keys.C))
            {
                GameGlobals.ShowCollisions = !GameGlobals.ShowCollisions;
            }
            */

            if (KeyboardManager.IsKeyPressedOnce(Keys.Enter))
            {
                mbDisableUpdate = !mbDisableUpdate;
                if (mbDisableUpdate)
                {
                    SetState(States_PRSRP.kPaused);
                }
                else
                {
                    SetState(miOldState);
                }
            }

            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) Game.Instance.Exit();
        }

        #region Core Properties

        // Graphics Device Manager
        private GraphicsDeviceManager mpGraphics;

        // SpriteBatch
        private SpriteBatch mpSpriteBatch;
        public SpriteBatch SpriteBatch
        {
            get { return mpSpriteBatch; }
        }

        // Game Time
        private GameTime mpGameTime;
        public GameTime GameTime
        {
            get { return mpGameTime; }
            set { mpGameTime = value; }
        }

        // Default Sprite
        public SpriteFont Arial;

        // Current map
        // Todo: Make Private
        private BaseMap mpCurMap;
        public BaseMap CurrentLevel
        {
            get { return mpCurMap; }
        }


        #endregion

        #region Default Constructor(s)

        /// <summary>
        /// Initialize the Game Engine.
        /// </summary>
        public GameEngine(Game _pGame)
        {
            // Initialize GraphicsDeviceManager
            _pGame.Content.RootDirectory = "Content";
            mpGraphics = new GraphicsDeviceManager(_pGame);
            mpGraphics.ApplyChanges();
            mpSpriteBatch = new SpriteBatch(_pGame.GraphicsDevice);
        }

        #endregion

        #region Debug Methods

        private Vector2 mvDebugPencilPos;

        void ResetDebugPencil()
        {
            mvDebugPencilPos = new Vector2(12, 12);
        }

        void DebugPrint(String _sTest)
        {
            mpSpriteBatch.DrawString(Arial, _sTest, mvDebugPencilPos, Color.White);
            mvDebugPencilPos.Y += 14;
        }

        void DebugDrawLine(String _sTest, int _iLength)
        {
            for (Int16 i = 0; i < _iLength; i++)
            {
                _sTest += "-";
            }
            DebugPrint(_sTest);
        }

        protected void ShowInfos()
        {
            ResetDebugPencil();
            DebugPrint("[ Press backspace to start debug mode ]");
            DebugPrint("");
            DebugPrint("Press [Up] while falling to slow down fall.");
            DebugPrint("Press [Down] while falling to accelerate fall.");
            DebugPrint("Press [L] to levitate.");
            DebugPrint("Press [Space] to shoot. (wip)");
            DebugPrint("Press [Enter] to pause. (wip)");
            DebugPrint("");
            DebugPrint("Press [A] to spawn ammo.");
            //DebugPrint("Press [C] to show cages.");
            DebugPrint("Press [E] to spawn enemies.");
        }

        protected void ShowPausedInfos()
        {
            ResetDebugPencil();
            DebugPrint("[ Game Paused ]");
        }

        protected void ShowDebugInfos()
        {
            ResetDebugPencil();
            DebugPrint(GameGlobals.GameTitle);

            // Print Heros Infos
            if (mpCurMap.Characters.Count > 0)
            {
                DebugPrint("");
                DebugPrint("Hero Score: " + mpCurMap.Characters[0].miScore);
                DebugPrint("Hero State: " + mpCurMap.Characters[0].miCurState);
                DebugPrint("Hero Pos: " + mpCurMap.Characters[0].mvPos);
                DebugPrint("Hero Speed: " + mpCurMap.Characters[0].mvSpeed);
                DebugPrint("Hero Health: " + mpCurMap.Characters[0].miHP + " / " + mpCurMap.Characters[0].miMaxHP);
                DebugPrint("Hero Ammo: " + mpCurMap.Characters[0].miAmmo + " / " + mpCurMap.Characters[0].miMaxAmmo);
            }

            /*
            // Print GameTime Infos
            DebugPrint("Elapsed Game Time: " + (int)GameTime.TotalGameTime.TotalMilliseconds + " ms");
            DebugPrint("");
            */

            // Print Enemies Infos
            if (mpCurMap.Enemies.Count > 0)
            {
                DebugPrint("");
                DebugPrint("Enemy State: " + mpCurMap.Enemies[0].miCurState);
                DebugPrint("Enemy Health: " + mpCurMap.Enemies[0].miHP + " / " + mpCurMap.Enemies[0].miMaxHP);
            }
            /*
            DebugPrint("");
            DebugPrint("Nb of Projectiles: " + AssetsManager.mtabHeroProjectiles.Count);
            DebugPrint("Nb of Enemies: " + AssetsManager.mtabEnemies.Count);
            DebugPrint("Nb of Collectables: " + AssetsManager.mtabCollectables.Count);
            */
        }

        #endregion

        #region BaseStateMachine Overrides

        // Game States Definition
        enum States_PRSRP
        {
            kLoading,
            kPlaying,
            kPaused,
            kDebugging
        }

        public override void OnInit()
        {
            mbDisableRender = true;
        }

        public void OnLoading()
        {
            // Todo: Add Loading Screen (if necessary)
            mbDisableRender = false;
            SetState(States_PRSRP.kPlaying);
        }

        public void OnPlaying()
        {
            // Handle Keyboard
            if (KeyboardManager.IsKeyPressedOnce(Keys.Back))
            {
                SetState(States_PRSRP.kDebugging);
            }
        }

        public void OnPaused()
        {
            /*
            if (pCurKeyboardState.IsKeyDown(Keys.Up)) { CurrentLevel.Characters[0].mvPos.Y -= 3; }
            if (pCurKeyboardState.IsKeyDown(Keys.Down)) { CurrentLevel.Characters[0].mvPos.Y += 3; }
            if (pCurKeyboardState.IsKeyDown(Keys.Left)) { CurrentLevel.Characters[0].mvPos.X -= 3; }
            if (pCurKeyboardState.IsKeyDown(Keys.Right)) { CurrentLevel.Characters[0].mvPos.X += 3; }
            */
        }

        public void OnDebugging()
        {
            // Handle Keyboard
            if (KeyboardManager.IsKeyPressedOnce(Keys.Back))
            {
                SetState(States_PRSRP.kPlaying);
            }
        }

        public void OnDebugging_Pre()
        {
            GameGlobals.ShowCollisions = true;
            Game.Editor.Enabled = true;
            Game.Instance.IsMouseVisible = true;
        }

        public void OnDebugging_Post()
        {
            GameGlobals.ShowCollisions = false;
            Game.Editor.Enabled = false;
            Game.Instance.IsMouseVisible = false;
        }

        public override void LoadStates()
        {

            //DefineState(States_PRSRP.kInit, OnInit);
            DefineState(States_PRSRP.kLoading, OnLoading);
            DefineState(States_PRSRP.kPlaying, OnPlaying);
            DefineState(States_PRSRP.kPaused, OnPaused);
            DefineState(States_PRSRP.kDebugging, OnDebugging, OnDebugging_Pre, OnDebugging_Post);

            SetState(States_PRSRP.kPlaying);
        }

        #endregion

        #region Xna Functions Overrides

        /// <sumary>
        /// Remove Depth Buffer from any created device.
        /// source: ms-help://MS.VSCC.v90/MS.VSIPCC.v90/MS.XNAGS31.1033/XNA/GraphicsDeviceManager_Framework_Xna_Microsoft_T.htm
        /// </sumary>
        void Callback_Graphics_PreparingDeviceSettings(object sender, PreparingDeviceSettingsEventArgs e)
        {
            e.GraphicsDeviceInformation.PresentationParameters.EnableAutoDepthStencil = false;
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic related content.
        /// Calling base.Initialize will enumerate through any components and initialize them as well.
        /// </summary>
        public void Initialize()
        {
            // Config Environement
            mpGraphics.IsFullScreen = GameGlobals.FullScreen;
            mpGraphics.PreferredBackBufferWidth = GameGlobals.GameWidth;
            mpGraphics.PreferredBackBufferHeight = GameGlobals.GameHeight;
            mpGraphics.ApplyChanges();

            LoadStates(); // todo: use base class constructor for this...

            // Add Event Handlers
            mpGraphics.PreparingDeviceSettings += new EventHandler<PreparingDeviceSettingsEventArgs>(Callback_Graphics_PreparingDeviceSettings);
        }

        /// <sumary>
        /// LoadContent will be called once per game and is the place to load all of your content.
        /// </sumary>
        public void LoadContent()
        {
            // Load Fonts
            Arial = Game.Instance.Content.Load<SpriteFont>("Arial");

            // Create a new SpriteBatch, which can be used to draw textures.
            mpSpriteBatch = new SpriteBatch(Game.Instance.GraphicsDevice);

            DrawingFunctions.mpRedDot = Game.Instance.Content.Load<Texture2D>("RedDot") as Texture2D;

            // Load Test Level
            mpCurMap = new Map_Debug();
        }

        /// <sumary>
        /// UnloadContent will be called once per game and is the place to unload all content.
        /// </sumary>
        public void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world, checking for collisions, gathering input, and playing audio.
        /// </summary>
        public void Update(GameTime _pGameTime)
        {
            // Update Game Time
            mpGameTime = _pGameTime;

            // Update Keyboard
            KeyboardManager.UpdateKeyboardState();
            HandleKeyboardInputs();

            // Update Mouse
            MouseManager.UpdateMouseState();

            // Update Engine State Machine
            ExecuteStateFunction();

            // Update Editor
            Game.Editor.Update();

            if (!mbDisableUpdate)
            {
                mpCurMap.Update();
            }
        }

        /// <sumary>
        /// This is called when the game should draw itself.
        /// </sumary>
        public void Draw(GameTime _pGameTime)
        {
            // Update Game Time
            mpGameTime = _pGameTime;

            Game.Instance.GraphicsDevice.Clear(Color.Black);

            // Start Sprite Drawing Operation
            mpSpriteBatch.Begin();

            if (!mbDisableRender)
            {
                mpCurMap.Draw();
            }

            // Show Infos
            switch (miCurState)
            {
                case (int)(States_PRSRP.kPlaying):
                    {
                        ShowDebugInfos();
                        break;
                    }
                case (int)(States_PRSRP.kDebugging):
                    {
                        ShowInfos();
                        break;
                    }
                case (int)(States_PRSRP.kPaused):
                    {
                        ShowPausedInfos();
                        break;
                    }
            }

            // Draw Editor.
            Game.Editor.Draw();

            // End Sprite Drawing Operation
            mpSpriteBatch.End();
        }

        #endregion

        /// <remarks>
        /// Todo: Add time propertie for slow-mo effects.
        /// Todo: Set thoses variables privates.
        /// </remarks>
        public bool mbDisableRender = false;
        public bool mbDisableUpdate = false;
    }
}
